module Stonean
  module Code
    class << self
			def controller_path
				File.join("app", "controllers")
			end

			def model_path
				File.join("app", "models")
			end

			def view_path
				File.join("app", "views")
			end
      
			def lockdown_initializer_path
				File.join("config", "initializers", "lockdown")
			end

			def migration_path
				File.join("db", "migrate")
			end

			def spec_path
				"spec"
			end
      
      def template_files
        all_relative_files.collect{|entry| File.join("templates", entry) }
      end
      
      def models
				build_file_list(model_path, "profile.rb",
                                    "permission.rb",
                                    "user.rb",
                                    "user_group.rb")
      end

			def models_by_relative_path
				models.collect{|entry| relative_path(entry) } 
      end
      
      def lockdown_initializers
				build_file_list(lockdown_initializer_path, "access.rb", "session.rb")
      end
      
			def lockdown_initializers_by_relative_path
				lockdown_initializers.collect{|entry| relative_path(entry)} 
			end

      def migrations
        build_file_list(migration_path, "create_profiles.rb",
																				"create_system_user.rb",
																				"create_users.rb",
																				"create_user_groups.rb",
																				"create_permissions.rb",
																				"create_base_user_groups.rb")
      end

			def migrations_by_relative_path
				migrations.collect{|entry| relative_path(entry)} 
			end

			def specs
        build_file_list spec_path, "lockdown_test_helper.rb"
			end

			def specs_by_relative_path
				specs.collect{|entry| relative_path(entry)} 
			end

      private
				def all_relative_files
					models_by_relative_path +
					lockdown_initializers_by_relative_path +
					migrations_by_relative_path +
					specs_by_relative_path 
				end

        def relative_path(hsh)
          File.join hsh[:dir], hsh[:file]
        end
        
        def build_file_list(dir, *files)
          rvalue = []
          files.each do |f|
            rvalue << {:dir => dir, :file => f}
          end
         rvalue 
        end
    end #end class block
    
    require "find"
    require "fileutils"

    class Deploy

			CURRENT_PATH = File.expand_path(File.dirname(__FILE__))
			GEM_ROOT = File.join CURRENT_PATH, "..", ".."	
      TEMPLATE_PATH = File.join GEM_ROOT, "templates"
                            

      
      def validate_environment(argv)
        if argv.empty?
          abort "Please specify the directory to lockdown, e.g. `#{File.basename($0)} .'"
        elsif !File.exists?(argv.first)
          abort "`#{argv.first}' does not exist."
        elsif !File.directory?(ARGV.first)
          abort "`#{argv.first}' is not a directory."
        elsif argv.length > 1
          abort "Too many arguments; please specify only the directory to lockdown."
        end
        
        @destination = argv[0]
        
        found_files = find_existing_files
        unless found_files.empty?
          error = <<-ERR
            \n
            Template files found in current project. Please remove
            or rename files for merging after installation
            \n
            Files found:
            #{found_files.join("\t")}
          ERR
          abort(error)
        end
      end # validate_environment
      
      
      def find_existing_files
				found_files = []

        Stonean::Code.models_by_relative_path.each do |source|
					found_files << source if target_exists?(source)
        end

        Stonean::Code.lockdown_initializers_by_relative_path.each do |source|
					found_files << source if target_exists?(source)
        end

        found_files
      end # find_existing_files
      
      def copy_files

				puts "[models] Copying models into your project."
				copy_templates Stonean::Code.models 

				puts "[initializers] Creating lockdown subdirectory."
				maybe_create_path Stonean::Code.lockdown_initializer_path

				puts "[initializers] Copying lockdown initializers into your project."
				copy_templates Stonean::Code.lockdown_initializers

				maybe_copy_spec_helper

				maybe_copy_migrations
      end
      
      
      private
				def find_current_schema_version
					versions = []
					contents_of(Stonean::Code.migration_path) do |c|
						versions << File.basename(c).split("_")[0]
          end
					(versions.length > 0 ? versions.sort.last.to_i : 1)
        end

        def contents_of(path)
          Find.find(path) do |content|
            #skip the starting directory
            next if content == path
            if File.directory?(content)
              Find.prune       # Don't look any further into this directory.
            else
              yield content
            end
          end
        end
        
				def maybe_create_path(path)
					FileUtils.mkdir_p(path, :mode => 0755) unless File.exists? path
        end

				def maybe_copy_spec_helper
					return unless File.exists?(Stonean::Code.spec_path)
					puts "[rspec] Spec path found. Copying helper"
					copy_templates Stonean::Code.specs
				end

				def maybe_copy_migrations
					puts "[migrations] Checking for migrate subdir."
					maybe_create_path Stonean::Code.migration_path

					puts "[migrations] Finding current schema version."
					current_schema_version = find_current_schema_version
					puts "[migrations] Current schema version: #{current_schema_version}"

					puts "[migrations] Copying migrations."
					Stonean::Code.migrations.each do |hsh|
						next if File.fnmatch?("*#{hsh[:file]}",hsh[:dir])
						src  = File.join(TEMPLATE_PATH, hsh[:dir], hsh[:file])
						current_schema_version += 1
						fname = sprintf("%03d",current_schema_version) << "_" << hsh[:file]
						dest = File.join(hsh[:dir], fname)
						FileUtils.cp(src, dest)
					  puts "[migrations] Added: #{dest}"
					end
				end

				def copy_templates(files)
					files.each do |hsh|
						src  = File.join(TEMPLATE_PATH, hsh[:dir], hsh[:file])
						dest = File.join(hsh[:dir],hsh[:file])
						FileUtils.cp(src, dest)
					end
				end

				def target_exists?(source)
					File.exists?(File.join(@destination, source))
				end

        def unindent(str)
          indentation = str[/\A\s*/]
          str.strip.gsub(/^#{indentation}/, "")
        end
    end
  end
end
